﻿/*
* Copyright (C) 2020 ~ 2024 chaigec All Rights Reserved.
*
* Author:     chaigec <chaigec@163.com>
* Maintainer: chaigec <chaigec@163.com>

 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
*/

#include "MkFile.h"
#include "MkLog.h"
#include "MkUtil.h"
#include "MkBase64.h"
#ifdef _WIN32
#include <io.h>
#else
#include <dirent.h>
#endif

#ifdef _WIN32

int mkdir(const char *path, int mode)
{
    return _mkdir(path);
}

DIR *opendir(const char *name)
{
    char namebuf[512];
    snprintf(namebuf, sizeof(namebuf), "%s\\*.*", name);

    WIN32_FIND_DATAA FindData;
    auto hFind = FindFirstFileA(namebuf, &FindData);
    if (hFind == INVALID_HANDLE_VALUE) {
        return nullptr;
    }
    DIR *dir = (DIR *)malloc(sizeof(DIR));
    memset(dir, 0, sizeof(DIR));
    dir->dd_fd = 0;
    dir->handle = hFind;
    return dir;
}

struct dirent *readdir(DIR *d)
{
    HANDLE hFind = d->handle;
    WIN32_FIND_DATAA FileData;

    if (!FindNextFileA(hFind, &FileData)) {
        return nullptr;
    }
    struct dirent *dir = (struct dirent *)malloc(sizeof(struct dirent) + sizeof(FileData.cFileName));
    strcpy(dir->d_name, (char *)FileData.cFileName);
    dir->d_reclen = (uint16_t)strlen(dir->d_name);
    if (FileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
        dir->d_type = 2;
    } else {
        dir->d_type = 1;
    }
    if (d->index) {
        free(d->index);
        d->index = nullptr;
    }
    d->index = dir;
    return dir;
}

int closedir(DIR *d)
{
    if (!d) {
        return -1;
    }
    if (d->handle != INVALID_HANDLE_VALUE) {
        FindClose(d->handle);
        d->handle = INVALID_HANDLE_VALUE;
    }
    if (d->index) {
        free(d->index);
        d->index = nullptr;
    }
    free(d);
    return 0;
}
#endif


Uint32 CMkFile::GetLogPath(MkString &strPath)
{
    MkString ModulePath;
    GetModuleFilePath(ModulePath);
    strPath = ModulePath + "/log";
    return NoneError;
}

Uint32 CMkFile::GetConfigPath(MkString &strPath)
{
    MkString ModulePath;
    GetModuleFilePath(ModulePath);
    strPath = ModulePath + "/config";
    return NoneError;
}

Uint32 CMkFile::GetDownloadPath(MkString &strPath)
{
    MkString ModulePath;
    GetModuleFilePath(ModulePath);
    strPath = ModulePath + "/download";
    return NoneError;
}

Uint32 CMkFile::GetFontFilePath(MkString &strPath)
{
    MkString ModulePath;
    GetModuleFilePath(ModulePath);
    strPath = ModulePath + "/ttc";
    return NoneError;
}

Uint32 CMkFile::GetFileSize(const MkString &strFileName)
{
    Uint32 FileSize = 0;
    FILE *fp = fopen(strFileName.c_str(), "r");
    if (!fp) {
        MkErrorLog("can not open file:%s\n", strFileName.c_str());
        return 0;
    }

    fseek(fp, 0, SEEK_END);
    FileSize = static_cast<Uint32>(ftell(fp));
    fclose(fp);
    return FileSize;
}

Uint32 CMkFile::ReadFileBuffer(const MkString &FileName, MkString &Content)
{
    Uint32 FileSize = GetFileSize(FileName);
    FILE *fp = fopen(FileName.c_str(), "r");
    if (!fp) {
        MkErrorLog("read file failed\n", FileName.c_str());
        return OpenFileFailed;
    }
    char *pbuf = static_cast<char *>(malloc(FileSize + 2));
    memset(pbuf, 0, FileSize + 2);
    fread(pbuf, 1, FileSize + 2, fp);
    Content = pbuf;
    free(pbuf);
    fclose(fp);
    return NoneError;
}

Uint32 CMkFile::WriteFileBuffer(const MkString &FileName, const MkString &FileBuffer)
{
    FILE *fp = fopen(FileName.c_str(), "wb");
    if (!fp) {
        MkErrorLog("open write file(%s) failed\n", FileName.c_str());
        return OpenFileFailed;
    }
    fwrite(FileBuffer.c_str(), 1, FileBuffer.size(), fp);
    fclose(fp);
    return NoneError;
}

Uint32 CMkFile::WriteBase64FileBuffer(const MkString &FileName, const MkString &Base64Buffer)
{
    Uint32 ErrorCode = NoneError;
    Uint32 Len = 0;
    MkString strDecode;
    ErrorCode = MkBase64Decode(Base64Buffer, strDecode);
    MkCheckErrorLog(ErrorCode, "decode base 64 buffer failed\n", Base64Buffer.c_str());
    return WriteFileBuffer(FileName, strDecode);
}


Uint32 CMkFile::CreateBasePath(const MkString &strPath)
{
    MkString tmpPath;
    MkString strLeft;
    MkString strRight = strPath;
    size_t ifind = strPath.find('/');
    if (MkString::npos == ifind) {
        ifind = strPath.find('\\');
    }

    while (MkString::npos != ifind && !strRight.empty()) {
        strLeft = strRight.substr(0, ifind);
        tmpPath += strLeft;
        tmpPath += '/';
        MakeDir(tmpPath.c_str());
        strRight = strRight.substr(ifind + 1, strRight.size());
        ifind = strRight.find('/');
        if (MkString::npos == ifind) {
            ifind = strRight.find('\\');
            if (MkString::npos == ifind && strRight.size() > 0) {
                tmpPath += strRight;
                tmpPath += '/';
                MakeDir(tmpPath.c_str());
            }
        }
    }
    return NoneError;
}

Uint32 CMkFile::BackUpFile(const MkString &strFrom, const MkString &strTo)
{
#ifdef _WIN32
    MkString strModelPath;
    MkString strExeFileName;
    GetModuleFilePath(strModelPath);
    strExeFileName = strModelPath + "\\7zr.exe";
    MkString strCmd = strExeFileName + " a -t7z " + strTo + " " + strFrom;
    system(strCmd.c_str());
#else
    MkString strCmd = "tar -czpf " + strTo + " " + strFrom;
    printf("back up log files cmd:%s\n", strCmd.c_str());
    system(strCmd.c_str());
#endif
    return NoneError;
}

Uint32 CMkFile::MakeDir(const MkString &strPath)
{
#ifdef _WIN32
    if ((-1 == _access(strPath.c_str(), 0)) && (-1 == _mkdir(strPath.c_str())))
#else
    if (mkdir(strPath.c_str(), S_IRUSR | S_IWUSR | S_IXUSR))
#endif
    {
        MkErrorLog("make dir(%s) failed\n", strPath.c_str());
        return CreateFolderFailed;
    }
    return NoneError;
}

void CMkFile::RemoveDir(const MkString& strPath)
{
#ifdef _WIN32
    _rmdir(strPath.c_str());
#else
    rmdir(strPath.c_str());
#endif
}

void CMkFile::DeleteLocalFile(const MkString &strFileName)
{
    remove(strFileName.c_str());
}

BOOL CMkFile::CheckFileIsExist(const MkString &strFileName)
{
    if (strFileName.empty()) {
        MkErrorLog("file name is nullptr\n");
        return FALSE;
    }

#ifdef _WIN32
    if (0 == _access(strFileName.c_str(), 0))
#else
    if (0 == access(strFileName.c_str(), 0))
#endif
    {
        return TRUE;
    }
    MkErrorLog("not find file:%s\n", strFileName.c_str());
    return FALSE;
}

BOOL CMkFile::IsDir(const MkString& strFilePath)
{
    DIR* pDir = opendir(strFilePath.c_str());
    if (!pDir) {
        return FALSE;
    }
    closedir(pDir);
    return TRUE;
}

BOOL CMkFile::CheckDirIsExist(const MkString &strPath)
{
    return CheckFileIsExist(strPath);
}

Uint32 CMkFile::GetModuleFilePath(MkString &strPath)
{
    char FileName[MAX_PATH] = { 0 };
#ifdef _WIN32
    GetModuleFileNameA(nullptr, FileName, MAX_PATH);
    char *ptmp = strrchr(FileName, '\\');
    if (!ptmp) {
        MkErrorLog("not find filepath \\ \n");
        return InvalidParams;
    }
    *ptmp = '\0';
#else
    readlink("/proc/self/exe", FileName, MAX_PATH);
    char *p = nullptr;
    if (nullptr != (p = strrchr(FileName, '/'))) {
        *p = '\0';
    } else {
        strPath = "";
    }
#endif
    strPath = FileName;
    return NoneError;
}

Uint32 CMkFile::ScanDir(const MkString& strPath, MkVectorString& vecChild)
{
    DIR *pDir = opendir(strPath.data());
    if (!pDir) {
        MkErrorLog("opendir :%s failed\n", strPath.c_str());
        return OpenFloderFailed;
    }

    dirent *pDirent = nullptr;
    MkString strChild;
    while (nullptr != (pDirent = readdir(pDir)) && nullptr != pDirent->d_name) {
        if (0 == strcmp(pDirent->d_name, "..")
            || 0 == strcmp(pDirent->d_name, ".")
            || pDirent->d_name[0] == '.') {
            continue;
        }
        strChild = strPath + "/" + pDirent->d_name;
        if (IsDir(strChild)) {
            continue;
        }
        vecChild.push_back(strChild);
    }
    closedir(pDir);
    return NoneError;
}
